Apple, the Apple logo, and Macintosh are registered trademarks of Apple Computer, Inc.
Mac and OpenDoc are trademarks of Apple Computer, Inc.
Introduction
This document describes how part editors deal with menus and the menu bar.
The Base Menu Bar
The OpenDoc shell application (or a container application) creates an ODMenuBar object and installs it as the base menu bar using ODWindowState::SetBaseMenuBar(). The base menu bar contains the Apple, Document, Edit, Help and Application menus.
Note: A container application might install a reduced menu bar as the base menu bar for embedded parts. When no embedded part is active, the container application would have its full menu bar.
Adding Part Menus to the Base Menu Bar
When a part is initialized (or when it is first grabbing the menu focus), it should create its own menu bar object by copying the base menu bar using ODWindowState::CopyBaseMenuBar(), and then adding its own menus (menu handles on the Mac OS).
void ClockPart::BuildMenuBar(Environment* ev)
{
if ((fMenuBar == kODNULL) || !(fMenuBar->IsValid(ev)))
Note: fFontMenu etc. are menu handles loaded from a resource. See the recipe on handling resources in OpenDoc.
New in DR4: If menus are inserted and removed from a menu bar object which is the current displayed menubar, the menu bar is redrawn, so it is not necessary to call ODMenuBar::Display().
Registering Command IDs
The Mac™ OS does not have position-independent IDs for menu items. OpenDoc allows part editors to register a position-independent Command ID for each menu and item using ODMenuBar::RegisterCommand(). Ideally, a part editor or part editor framework would capture the mapping from menu items to command IDs in a resource so that the part editor's menus can be rearranged without altering the code or recompiling.
The OpenDoc shell registers commands for the items in the Apple, Document and Edit menus, some of which are handled by part editors (eg. Print and View In Window). Part editors can add items to the end of the Document and Edit menus, but are discouraged from reordering the items of the base menu bar. If a part editor does reorganizes those menus, it must reregister the command numbers so that the shell can still handle menu items correctly.
New in DR2: A range of command IDs from kODCommandShellFirst ( = 1000) to kODCommandShellLast (= 20000) is reserved for OpenDoc shells and container applications. Part editors should use IDs above 20000.
New in DR2: Command IDs can also represent menus as a whole (using 0 as the item). The OpenDoc shell registers
kODCommandAppleMenu, kODCommandDocumentMenu and kODCommandEditMenu in addition to the items in those menus. These commands can be used to enable or disable entire menus.
Menu IDs
Macintosh menu IDs are positive shorts. Negative values are reserved by the System. Hierarchical menus must be in the 1 byte range.
Menu IDs can be the same as the resource ID, but do not have to be. All menus installed in the menu bar must have unique IDs. Thus there is a potential conflict between the shell, shell plug-ins and the active part. The following ranges are reserved:
Container Applications or the OpenDoc Document Shell: 255-16383
Container Applications or the OpenDoc Document Shell: 0-127
Plug-Ins/Services: 128-193 (dynamically assigned)
Part Editors: 194-254
Since there may be multiple plug-ins, they must assign IDs dynamically, by choosing an ID, looking for a menu with that ID, and if one is found, adding 1 to the ID and trying again.
New in DR4: Note that 255 is reserved for the shell, not for hierarchical menus of part editors. There was an inadvertent overlap in the DR3 version of this recipe.
Claiming the Menu Bar
When a part activates itself, it should request the menu focus (along with other foci). Once the part has the menu focus, it can call ODMenuBar::Display to make its menu bar active. But first it should check if the menu bar is still valid (i.e. the base menu bar has not been changed.) If is has been changed, it should be re-copied.
OpenDoc turns a mouse-down in the menu bar, or a command-key into an event of type kODEvtMenu. The message field of the event contains the menu result value returned by MenuSelect() or MenuKey(). The high word of this value is the menu, the low word is the menu item. The command ID can be obtained by calling ODMenuBar::GetCommand(). If no command has been registered, a synthetic command id is generated.
if (fClockPart->GetMenuBar()->IsCommandSynthetic(ev, command))// Font etc.
{
…
}
else
{
switch (command)
{
…
case kODCommandAbout:
fClockPart->ShowAboutBox(ev, fFrame);
break;
case cSynchronize:
fClockPart->ShowSynchDialog(ev);
break;
case cShowSettings:
fClockPart->ShowSettingsDialog(ev);
break;
default:
wasHandled = kODFalse;
break;
}
}
return wasHandled;
}
Changing Menu Appearance
When the mouse is clicked in the menu bar, OpenDoc determines which part has the menu focus, and calls Part::AdjustMenus. There are several ODMenuBar methods for changing menu item appearance by command number. A part can also get hold of the menu handle and use Toolbox calls.
Note: Part Editors will also need to disable certain items when a draft is read-only. See the Init Part and Externalize recipe for information on draft permissions.
Disabling All Menus
When a moveable modal dialog is displayed, all except Apple and Process menus must be disabled, and re-enabled when the dialog is dismissed. The ODMenuBar::DisableAll() and EnableAll() methods can be used for this purpose. If a moveable modal dialog has a text field in it, the Edit menu will need to be enabled for Cut/Copy/Paste, so the part editor might do something like the following, before showing the dialog:
The Page Setup and Print menu items need to be handled by the root part of the active window, even if an embedded frame is active and has the menu focus. As a result the Dispatcher calls the root part's HandleEvent method if the part with the menu focus does not handle the event in its HandleEvent method, and provided the two frames involved are different.
For symmetry, the AdjustMenus method of the root part of the active window is also called before that of the part with the menu focus, provided the two frames involved are different.
Container parts should check if they have the menu focus when adjusting menu items, and should be careful not to enable root menu items like Print when embedded.
Shared Menu Bar
It is possible to use CFM per-context globals to share the menu bar object between several instances of the same part in a single document. The official Sample Part shows one way to do this. You may wish to define your own reference-counted C++ object to hold several such pieces of information that can be shared by multiple instances of a part. See the Shared Utility Windows recipe.